package org.redmagic.service.dao;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;

import javax.annotation.Nonnull;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.redmagic.service.domain.Property;
import org.springframework.stereotype.Repository;

import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;

@Repository
public class InMemoryPlaceholderDao implements PlaceholderDao, GroupDao {

	private static final Log LOG = LogFactory.getLog(InMemoryPlaceholderDao.class);
	
	private final Multimap<String, Property> groups;
	private final ConcurrentHashMap<String, ConcurrentHashMap<Set<String>, ConcurrentHashMap<String, String>>> properties;
	
	public InMemoryPlaceholderDao() {
		this.groups = HashMultimap.create();
		this.properties = new ConcurrentHashMap<String, ConcurrentHashMap<Set<String>,ConcurrentHashMap<String,String>>>();
	}
	
	public void populate() {
		
		LOG.info("Populating with fake data.");
		
		// fake data
		String placeholder = "database/user";
		Set<String> tags = new TreeSet<String>();
		tags.add("systemType=development");
		tags.add("accessLevel=restricted");
		String publicGroup = "public";
		String projectGroup = "project";
		String publicValue = "PUBLIC_VALUE1";
		String projectValue = "PROJECT_VALUE1";
		
		post(new Property(placeholder, tags, publicGroup, publicValue));
		
		post(new Property(placeholder, tags, projectGroup, projectValue));
		post(new Property("database/password", tags, projectGroup, "DEV_PASSWD"));
		
		populateWithIndividualRequestFakeData();
	}
	
	
	private void populateWithIndividualRequestFakeData() {
        String dbUser = "database/user";
        String dbPassword = "database/password";
        String dbHost = "database/host";
        Set<String> tagsBoth = Sets.newHashSet("systemType=development", "release=1.0");
        Set<String> tagsRelease = Sets.newHashSet("release=1.0");
        
        post(new Property(dbUser, tagsRelease, "public", "PUBLIC_REL_1.0_USER"));
        post(new Property(dbPassword, tagsBoth, "public", "PUBLIC_DEV_REL_1.0_PASSWORD"));
        post(new Property(dbHost, tagsRelease, "public", "PUBLIC_REL_1.0-localhost"));
    }

    @Override
	public String get(Property property) {
		
		LOG.info("Handling request for..." + property);
		
		String placeholder = property.getPlaceholder();
		Set<String> tags = property.getTags();
		String group = property.getGroup();
		
		String value = null;
		if (this.properties.containsKey(placeholder)) {
			
			// check if the placeholder exists
			ConcurrentHashMap<Set<String>, ConcurrentHashMap<String, String>> tagsPerGroup = this.properties.get(placeholder);
			
			if (tagsPerGroup.containsKey(tags)) {
				
				// see if a property with those tags exists
				ConcurrentHashMap<String, String> valuePerGroup = tagsPerGroup.get(tags);
				
				// check the group and get the appropriate value
				value = valuePerGroup.get(group);
			}
		}		
		
		return value;
	}
	
	@Override
	public void put(Property property) {
		post(property);
	}

	@Override
	public void post(Property property) {
		
		String placeholder = property.getPlaceholder();
		Set<String> tags = property.getTags();
		String group = property.getGroup();
		String value = property.getValue();
		
		this.groups.put(group, property);
		
		if (this.properties.containsKey(placeholder)) {
			
			ConcurrentHashMap<Set<String>, ConcurrentHashMap<String, String>> tagsPerGroup = this.properties.get(placeholder);
			if (tagsPerGroup.containsKey(tags)) {
				
				ConcurrentHashMap<String, String> valuePerGroup = tagsPerGroup.get(tags);
				valuePerGroup.put(group, value);
				
			} else {
				
				ConcurrentHashMap<String, String> valuePerGroup = new ConcurrentHashMap<String, String>();
				valuePerGroup.put(group, value);
				
				tagsPerGroup.put(tags, valuePerGroup);
				
			}
			
		} else {
			
			ConcurrentHashMap<String, String> valuePerGroup = new ConcurrentHashMap<String, String>();
			valuePerGroup.put(group, value);
			
			ConcurrentHashMap<Set<String>, ConcurrentHashMap<String, String>> tagsPerGroup = new ConcurrentHashMap<Set<String>, ConcurrentHashMap<String,String>>();
			tagsPerGroup.put(tags, valuePerGroup);
			
			this.properties.put(placeholder, tagsPerGroup);
			
		}
	}


	@Override
	public void delete(Property property) {
		
		String placeholder = property.getPlaceholder();
		Set<String> tags = property.getTags();
		String group = property.getGroup();
		
		this.groups.remove(group, property);
		
		if (this.properties.containsKey(placeholder)) {
			
			ConcurrentHashMap<Set<String>, ConcurrentHashMap<String, String>> tagsPerGroup = this.properties.get(placeholder);
			if (tagsPerGroup.containsKey(tags)) {
				
				ConcurrentHashMap<String, String> valuePerGroup = tagsPerGroup.get(tags);
				valuePerGroup.remove(group);
			}
			
		}
		
	}

	@Override
	public List<Property> getProperties(String group) {
		Collection<Property> placeholders = this.groups.get(group);
		return new ArrayList<Property>(placeholders);
	}

	@Override
	@Nonnull
	public List<String> getGroups() {
		return new ArrayList<String>(this.groups.keySet());
	}

}
